package problem.ej1;

import static org.junit.Assert.assertEquals;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;

import org.junit.Test;

import problem.ej1.Ejercicio1;
import utilidades.Utils;
public class Ej1Test {

	private static Ejercicio1 coso1 = new Ejercicio1();
	
	@Test
	public void ej1Test() throws IOException
	{
		int[] lista = {10, 9, 8, 7, 6, 5, 4};
		int[] lista2 = {9, 8, 7, 6, 5, 4, 3, 2};
		int[] lista3 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4};
		int[] lista4 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 2};
		int[] lista5 = {1, 2, 3, 4};
		int[] lista6 = {1, 2, 3, 4, 1, 2, 3, 4, 2};
		int[] lista7 = {1, 2, 3, 4, 1, 2, 3, 5, 2};
		int[] lista8 = {1, 9};
		int[] lista9 = {9, 1};
		int[] lista10 = {4, 5, 6, 2, 10, 11, 1};
		int[] lista11 = {4, 5, 6, 2, 10, 1};
		int[] lista12 = {10, 4, 5, 6, 7, 1, 8, 9};
		int[] lista13 = {9, 4, 8, 5, 3, 2};
		int[] lista14 = {9, 4, 8, 5, 3, 2, 5};
				
		assertEquals(0, coso1.precio(lista.length, lista));
		assertEquals(0, coso1.precio(lista2.length, lista2));
		assertEquals(3, coso1.precio(lista3.length, lista3));
		assertEquals(3, coso1.precio(lista4.length, lista4));
		assertEquals(3, coso1.precio(lista5.length, lista5));
		assertEquals(3, coso1.precio(lista6.length, lista6));
		assertEquals(4, coso1.precio(lista7.length, lista7));
		assertEquals(8, coso1.precio(lista8.length, lista8));
		assertEquals(0, coso1.precio(lista9.length, lista9));
		assertEquals(9, coso1.precio(lista10.length, lista10));
		assertEquals(8, coso1.precio(lista11.length, lista11));
		assertEquals(8, coso1.precio(lista12.length, lista12));
		assertEquals(4, coso1.precio(lista13.length, lista13));		
		assertEquals(4, coso1.precio(lista14.length, lista14));
	}
	
	@Test
	public void TestCatedra() throws IOException
	{
		fileWritingRunTest("Tp1Ej1.in", "Tp1Ej1Resultados.out");
		fileTestingRunTest("Tp1Ej1.in", "Tp1Ej1Resultados.out");
	}
	
	@Test
	public void TestMiniEscalera() throws IOException
	{

		fileWritingRunCualquierTest("Tp1Ej1MiniEscaleras.in", generar_1Mini_xSinIntencion(100, 1, 100));
		fileWritingRunTest("Tp1Ej1MiniEscaleras.in", "Tp1Ej1MiniEscalerasResultados.out");
		fileTestingRunTest("Tp1Ej1MiniEscaleras.in", "Tp1Ej1MiniEscalerasResultados.out");
		testRendimiento("MiniEscaleras", generar_1Mini_xSinIntencion(300, 1, 1000), 0);
		
	}
		
	@Test
	public void TestSinIntencion() throws IOException
	{

		fileWritingRunCualquierTest("Tp1Ej1SinIntencion.in", generar_1Mini_xSinIntencion(10, 0, 10));
		fileWritingRunTest("Tp1Ej1SinIntencion.in", "Tp1Ej1SinIntencionResultados.out");
		fileTestingRunTest("Tp1Ej1SinIntencion.in", "Tp1Ej1SinIntencionResultados.out");
	}
	
	@Test
	public void TestMinimoAlFinal() throws IOException
	{
		fileWritingRunCualquierTest("Tp1Ej1MinimoAlFinal.in", generarMaximoYminimoEnLasPuntas_Minimo_Maximo(10, 1));
		fileWritingRunTest("Tp1Ej1MinimoAlFinal.in", "Tp1Ej1MinimoAlFinalResultados.out");
		fileTestingRunTest("Tp1Ej1MinimoAlFinal.in", "Tp1Ej1MinimoAlFinalResultados.out");
	}
	
	@Test
	public void TestMaximoAlPrincipio() throws IOException
	{
		
		fileWritingRunCualquierTest("Tp1Ej1MaximoAlPrincipio.in", generarMaximoYminimoEnLasPuntas_Minimo_Maximo(10, 2));
		fileWritingRunTest("Tp1Ej1MaximoAlPrincipio.in", "Tp1Ej1MaximoAlPrincipioResultados.out");
		fileTestingRunTest("Tp1Ej1MaximoAlPrincipio.in", "Tp1Ej1MaximoAlPrincipioResultados.out");
	
	}
	
	@Test
	public void TestMinimoYmaximoEnLasPuntas() throws IOException
	{
		fileWritingRunCualquierTest("Tp1Ej1MaximoMinimo.in", generarMaximoYminimoEnLasPuntas_Minimo_Maximo(10, 0));
		fileWritingRunTest("Tp1Ej1MaximoMinimo.in", "Tp1Ej1MaximoMinimoResultados.out");
		fileTestingRunTest("Tp1Ej1MaximoMinimo.in", "Tp1Ej1MaximoMinimoResultados.out");
	}
	
	@Test
	public void EscaleraAscendente() throws IOException
	{
		fileWritingRunCualquierTest("Tp1Ej1Ascendente.in", generarEscaleraAscendenteOdescendente(10, 1, 10));
		fileWritingRunTest("Tp1Ej1Ascendente.in", "Tp1Ej1AscendenteResultados.out");
		fileTestingRunTest("Tp1Ej1Ascendente.in", "Tp1Ej1AscendenteResultados.out");
	}
	
	@Test
	public void EscaleraDescendente() throws IOException
	{
		fileWritingRunCualquierTest("Tp1Ej1Descendente.in", generarEscaleraAscendenteOdescendente(10, 0, 10));
		fileWritingRunTest("Tp1Ej1Descendente.in", "Tp1Ej1DescendenteResultados.out");
		fileTestingRunTest("Tp1Ej1Descendente.in", "Tp1Ej1DescendenteResultados.out");
	}
	
	@Test
	public void GenerarSriptRendimiento() throws IOException
	{
		testRendimiento("SinIntencion", generar_1Mini_xSinIntencion(300, 0, 1000), 0);
	}
	
	@Test
	public void GenerarScriptPeorCaso() throws IOException
	{
		testRendimiento("PeorCaso", generarEscaleraAscendenteOdescendente(300, 1, 1000), 0);
	}
	
	@Test
	public void GenerarScriptMejorCaso() throws IOException
	{
		testRendimiento("MejorCaso", generarEscaleraAscendenteOdescendente(300, 0, 1000), 0);
	}
	
	@Test
	public void GenerarScriptComplejidadTeorica() throws IOException
	{
		testRendimiento("ComplejidadTeorica", generarEscaleraAscendenteOdescendente(300, 1, 1000), 1);
	}
	
	@Test
	public void GenerarScriptAscendenteDescendente() throws IOException
	{
		fileWritingRunCualquierTest("Tp1Ej1AscendenteDescendente.in", generarEscaleraAscendenteYdescendente(300, 1, 1000));
		fileWritingRunTest("Tp1Ej1AscendenteDescendente.in", "Tp1Ej1AscendenteDescendenteResultado.out");
		fileTestingRunTest("Tp1Ej1AscendenteDescendente.in", "Tp1Ej1AscendenteDescendenteResultado.out");
		
		fileWritingRunCualquierTest("Tp1Ej1DescendenteAscendente.in", generarEscaleraAscendenteYdescendente(300, 0, 1000));
		fileWritingRunTest("Tp1Ej1DescendenteAscendente.in", "Tp1Ej1DescendenteAscendenteResultado.out");
		fileTestingRunTest("Tp1Ej1DescendenteAscendente.in", "Tp1Ej1DescendenteAscendenteResultado.out");
		
		testRendimiento("AscendenteDescendente", generarEscaleraAscendenteYdescendente(300, 1, 1000), 0);
		testRendimiento("DescendenteAscendente", generarEscaleraAscendenteYdescendente(300, 0, 1000), 0);
	}
	
	
/**********************************************De Aca para abajo son todas funciones********************************************************/
/******************************************************************************************************************************************/

	public void fileWritingRunTest(String A, String B) throws IOException
	{
      // Con este test se lee el archivo de entrada de nombre A
      // y se genera la salida de nombre B
      BufferedReader is = new BufferedReader( new InputStreamReader( getClass().getResourceAsStream( A ) ) );
      BufferedWriter os = new BufferedWriter( new FileWriter( getClass().getResource( "" ).getPath() + B ) );
      
      String line;
      while ( ( line = is.readLine() ) != null ) 
      {
         os.append( Correr(line) ).append( '\n' );
      }
      os.close();
   }
	 
	   public void fileTestingRunTest(String A, String B) throws IOException {
	      // Con este test se compara un archivo de entrada con el formato de la catedra 
	      //    contra otro archivo con valores esperados
	      BufferedReader source  = new BufferedReader( new InputStreamReader( getClass().getResourceAsStream( A ) ) );
	      BufferedReader control = new BufferedReader( new InputStreamReader( getClass().getResourceAsStream( B ) ) );
	      
	      String line;
	      while ( ( line = source.readLine() ) != null )
	      {
	         assertEquals( control.readLine(), Correr( line ) );
	      }      
	   }
	   
		private String Correr(String a)
		{
			int[] lista = Utils.parseArrayI(a);
			new Ej1Test();
			return String.valueOf( Ej1Test.coso1.precio( lista.length , lista) );
		}
		
		//*******************************************************************************************************//
		// Este test me puede generar dos tipos distintos de matrices, una con miniEscaleras y otro sin ninguna intencion
		private static LinkedList<int[]> generar_1Mini_xSinIntencion(int cantListas, int tipo, int tamano)
		{
			/** x!=1) SinIntencion
			 * 	1) MiniEscaleras
			 */
			Random rnd = new Random(); ///el generador de numeros random que usaremos
			LinkedList<int[]> lista_res = new LinkedList<int[]>();///la lista de arreglos que devolveremos
			for(int i = 0; i <= cantListas; i++)
			{
					int longitud_lista_i =  Math.abs(rnd.nextInt()) % tamano;
					if (longitud_lista_i <= 1) // si nos devuelve 0 o 1, que por lo menos sea una lista de dos elementos.
					{
						longitud_lista_i = 6;
					}
					int[] lista_i;
					lista_i = new int[longitud_lista_i]; ///creamos el primer arreglo con su longitud
					
					if (tipo == 1) // quiero hacer miniescaleras
					{
						Random rnd_1 = new Random(); // este random lo uso para ver que tan larga son las escaleras.
						int longitud_escalera = (rnd_1.nextInt() % 5) + 1;
						int elem_prox = (Math.abs(rnd.nextInt()) % 9) + 1;
						int j = 0;
						int y = 0;
						while (j < longitud_lista_i)
						{	
							if( y == longitud_escalera)
							{
								// tengo que asignarle a i otro valor para longitud_escalera
								y = 0; //(rnd_1.nextInt() % 10) + 1;
							    elem_prox = (Math.abs(rnd.nextInt()) % 9) + 1;
							}
							else
							{					
								elem_prox = elem_prox + 7;										
							}
							lista_i[j] = elem_prox;
							y++;
							j++;
						}
					}
					else // quiero hacer listas sin ninguna intencion.
					{	
						for(int j = 0; j < longitud_lista_i; j++) // rellenamos cada arreglo.
						{
							int elem_prox = (Math.abs(rnd.nextInt()) % 9) + 1;								
							lista_i[j] = elem_prox;
						}
					}
				lista_res.add(lista_i);
			}
			return lista_res;	
		} 	
		
		public void fileWritingRunCualquierTest(String A, LinkedList<int[]> funcion) throws IOException
		{
	      // Con este test se genera segun el valor de s, un test con mini escaleras a lo largo del arreglo o un arreglo con numeros 
		  // sin ningun tipo de patron propuesto por nosotros y se genera el .in
	      BufferedWriter os = new BufferedWriter( new FileWriter( getClass().getResource( "" ).getPath() + A ) );  
	      LinkedList<int[]> casos = funcion;
	      Iterator<int[]> losCasos = casos.iterator();	  
			
	      while (losCasos.hasNext()) 
	      {		
	    	  String linea = "";
	    	  int[] UnCaso = losCasos.next();
				for(int i = 0; i < UnCaso.length; i++)
				{
					if (i == UnCaso.length - 1)
					{
						linea = linea.concat(String.valueOf(UnCaso[i]));
					}
					else
					{
						linea = linea.concat(String.valueOf(UnCaso[i]));
						linea = linea.concat(" ");	
					}	
				}	
			os.append(linea);	
	        os.newLine();
	      }
	      os.close();
		}
		
		private static LinkedList<int[]> generarEscaleraAscendenteOdescendente(int cantListas, int tipo, int tamano)
		{
			/** x!=1) Descendente
			 * 	1) Ascendente
			 *
			 */
			Random rnd = new Random(); ///el generador de numeros random que usaremos
			LinkedList<int[]> lista_res = new LinkedList<int[]>();///la lista de arreglos que devolveremos
			for(int i = 0; i <= cantListas; i++)
			{
				int longitud_lista_i =  Math.abs(rnd.nextInt()) % tamano;
				if (longitud_lista_i <= 1) // si nos devuelve 0 o 1, que por lo menos sea una lista de dos elementos.
				{
					longitud_lista_i = 2;
				}
				int[] lista_i;
				lista_i = new int[longitud_lista_i]; ///creamos el primer arreglo con su longitud
				int elem_prox = (Math.abs(rnd.nextInt()) % 12) + 1;
				if (tipo != 1)
				{
					elem_prox += 9000; // yo voy a hacer como mucho una lista de 30 elementos, entonces si a cada uno le resto 9, es como que el numero se tiene que 
					// aguantar que le hagan -9*200 = 1800,
				}
				for (int j = 0; j < longitud_lista_i; j++)
				{
					lista_i[j] = elem_prox;
					
					if (tipo == 1) // Ascendente
					{
						elem_prox += 9;
					}
					else // Descente
					{	
						elem_prox -= 9;
					}
				}
				lista_res.add(lista_i);
			}
			return lista_res;	
		} 	
		
		
		private static LinkedList<int[]> generarEscaleraAscendenteYdescendente(int cantListas, int tipo, int tamano)
		{
			/** x!=1) DescendenteAscendente
			 * 	1) AscendenteDescendente
			 *
			 */
			Random rnd = new Random(); ///el generador de numeros random que usaremos
			LinkedList<int[]> lista_res = new LinkedList<int[]>();///la lista de arreglos que devolveremos
			for(int i = 0; i <= cantListas; i++)
			{
				int longitud_lista_i =  Math.abs(rnd.nextInt()) % tamano;
				if (longitud_lista_i <= 1) // si nos devuelve 0 o 1, que por lo menos sea una lista de dos elementos.
				{
					longitud_lista_i = 2;
				}
				int[] lista_i;
				lista_i = new int[longitud_lista_i]; ///creamos el primer arreglo con su longitud
				int elem_prox = (Math.abs(rnd.nextInt()) % 12) + 1;
				if (tipo != 1)
				{
					elem_prox += 4400; // yo voy a hacer como mucho una lista de 1000 elementos, entonces si a cada uno le resto 9, es como que el numero se tiene que 
					// aguantar que le hagan -9*1000 = 9000,
				}
				int mitad = longitud_lista_i / 2;
				for (int j = 0; j < longitud_lista_i; j++)
				{
					lista_i[j] = elem_prox;
					if (j < mitad)
					{
						if (tipo == 1) // Ascendente
						{
							elem_prox += 9;
						}
						else // Descente
						{	
							elem_prox -= 9;
						}
					}
					else
					{
						if (tipo == 0) // AscendenteDescendente
						{
							elem_prox += 7;
						}
						else // DescenteAscendente
						{	
							elem_prox -= 7;
						}
					}
				}
				
				lista_res.add(lista_i);
			}
			return lista_res;	
		} 
		
		private static LinkedList<int[]> generarMaximoYminimoEnLasPuntas_Minimo_Maximo(int cantListas, int tipo)
		{
			// Esta funcion, puede generar 3 tipos de casos, maximo y minimos en las puntas
			
			/** 0) minimo y maximo
			 * 	1) minimo
			 * x>1) maximo
			 */
			Random rnd = new Random(); ///el generador de numeros random que usaremos
			LinkedList<int[]> lista_res = new LinkedList<int[]>();///la lista de arreglos que devolveremos
			for(int i = 0; i <= cantListas; i++)
			{
				int longitud_lista_i =  Math.abs(rnd.nextInt()) % 10;
				if (longitud_lista_i <= 1) // si nos devuelve 0 o 1, que por lo menos sea una lista de dos elementos.
				{
					longitud_lista_i = 2;
				}
				int[] lista_i;
				lista_i = new int[longitud_lista_i]; ///creamos el primer arreglo con su longitud
				int elem_prox = (Math.abs(rnd.nextInt()) % 12) + 2;
				int j = 0;
				if (tipo == 0) // minimoymaximo
				{
					lista_i[0] = 14;
					lista_i[longitud_lista_i - 1] = 1;
					longitud_lista_i--;
					j = 1;
				}
				else if( tipo == 1) //minimo 
				{
					lista_i[longitud_lista_i - 1] = 1;
					longitud_lista_i--;
					j = 0;
				}
				else // maximo
				{
					lista_i[0] = 14;
					 j = 1;
				}
					while (j < longitud_lista_i)
				{
					lista_i[j] = elem_prox;
					elem_prox = (Math.abs(rnd.nextInt()) % 12) + 2;
					j++;
				}
				lista_res.add(lista_i);
			}
			return lista_res;	
		} 
		
		public void testRendimiento(String nombArch, LinkedList<int[]> funcion, int tipo)throws IOException
		{
			/**
			 * tipo != 1 ==> uso el contador
			 * tipo == 1 ==> complejidaTeorica (tamano de las listas) 
			 */
			BufferedWriter os = new BufferedWriter( new FileWriter( getClass().getResource( "" ).getPath() + nombArch + ".txt" ) );  
		      LinkedList<int[]> casos = funcion;
		      Iterator<int[]> losCasos = casos.iterator();
			while(losCasos.hasNext()){
				Ejercicio1.contador = 0;
				int[] caso = losCasos.next();
				int tamano = caso.length;
				int resultado = coso1.precio(tamano,caso);
				String linea;
				if (tipo != 1)
				{
				 linea = String.valueOf(tamano) + " " +String.valueOf(Ejercicio1.contador);
				}
				else
				{
				 linea = String.valueOf(tamano) + " " +String.valueOf(tamano);		
				}
				os.append(linea);
				
				if (losCasos.hasNext() == true)
				{
					os.newLine();
				}
			}
			 os.close();
		}	
}
